﻿using System;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using DevelopTool.Bll.DbBlls;
using DevelopTool.Bll.OffLineBlls;
using DevelopTool.Common;
using DevelopTool.Model.OffLineModels;
using DevelopTool.OffLine.Model;
using static System.Windows.Forms.VisualStyles.VisualStyleElement;

namespace DevelopTool.OffLine.Bll
{
    /// <summary>
    /// 批量离线操作类
    /// </summary>
    public class BatchOfflineDataBll
    {

        /// <summary>
        /// 开始任务
        /// </summary>
        /// <param name="tableName"></param>
        public void Start(OffLineTaskParam offLineTaskParam)
        {
            string dataBaseName = offLineTaskParam.DataBaseName;
            if (!string.IsNullOrWhiteSpace(dataBaseName)) dataBaseName += ".";

            try
            {
                //检查表是否存在
                new OracleDbManagerBll(offLineTaskParam.dbInfo).ExecuteSqlSimple($"select * from {dataBaseName}{offLineTaskParam.TableName} where rownum=1");
            }
            catch (Exception error)
            {
                offLineTaskParam.Status = 5;
                offLineTaskParam.TaskName = offLineTaskParam.TableName;
                offLineTaskParam.TotalRowCount = 0;
                offLineTaskParam.CompleteTime = DateTime.Now;
                Log(error);
                return;
            }


            try
            {
                offLineTaskParam.Status = 1;
                offLineTaskParam.CloneObj();
                int offLineId = CreateOffLine(offLineTaskParam);//创建离线数据项，并返回id

                int pageSize = offLineTaskParam.PageSize;//每页条数

                int nowRunThread = 0;//当前已开启的线程数

                List<Task<int>> taskList = new List<Task<int>>();//正在执行的线程集合
                TaskFactory taskFactory = new TaskFactory(TaskCreationOptions.LongRunning, TaskContinuationOptions.None);

                for (int page = 1; page < int.MaxValue; page++)
                {
                    try
                    {
                        if (!CheckTaskStatus(offLineTaskParam)) break;//随时检查下任务状态

                        int startRow = (page - 1) * pageSize + 1;
                        int endRow = page * pageSize + 1;

                        string sql = $@"SELECT D.* FROM (SELECT ROWNUM AS ROWNO, ROWID AS DATAID, T.*
                                                FROM {dataBaseName}{offLineTaskParam.TableName} T ORDER BY ROWID ASC) D
                                        WHERE D.ROWNO BETWEEN {startRow} AND {endRow}";

                        int result = GetDataAndSave(sql, page, offLineId, offLineTaskParam);
                        offLineTaskParam.ExecutedRowCount += result;
                        if (result <= 0)
                        {
                            offLineTaskParam.Status = 2;
                            break;
                        }

                        #region 多线程

                        //Task<int> task = taskFactory.StartNew<int>(() => GetDataAndSave(sql, page, offLineId, offLineTaskParam));
                        //taskList.Add(task);

                        //nowRunThread++;//还没到指定线程数量就继续

                        //if (nowRunThread >= offLineTaskParam.ThreadNum)
                        //{
                        //    //等待已开启的所有线程执行完成
                        //    Task.WaitAll(taskList.ToArray(), 1800000);//超时时间半小时

                        //    offLineTaskParam.ExecutedRowCount += taskList.Sum(it => it.Result);//计算已执行的行数

                        //    if (taskList.Any(it => it.Result <= 0))
                        //    {
                        //        offLineTaskParam.Status = 2;
                        //        for (int t = 0; t < taskList.Count; t++)
                        //        {
                        //            taskList[t].Dispose();
                        //        }
                        //        taskList.Clear();
                        //        break;//如果有某个线程获取到0行数据，那就说明没有了，完成运行
                        //    }

                        //    nowRunThread = 0;//置0，继续开启线程

                        //    for (int t = 0; t < taskList.Count; t++)
                        //    {
                        //        taskList[t].Dispose();
                        //    }
                        //    taskList.Clear();
                        //}
                        #endregion
                    }
                    catch (Exception ex)
                    {
                        Log(ex);
                        offLineTaskParam.Status = 5;
                        break;
                    }
                }
            }
            catch (Exception error)
            {
                Log(error);
                offLineTaskParam.Status = 5;
            }
            finally
            {
                offLineTaskParam.CompleteTime = DateTime.Now;
            }
        }



        /// <summary>
        /// 查询数据并保存
        /// </summary>
        /// <param name="sql"></param>
        /// <param name="index"></param>
        /// <param name="offLineId"></param>
        /// <param name="compressType"></param>
        /// <param name="exceptionWaitTime"></param>
        /// <param name="exceptionTryNum"></param>
        /// <returns></returns>
        private int GetDataAndSave(string sql, int index, int offLineId, OffLineTaskParam offLineTaskParam)
        {
            try
            {
                DataTable dataTable = new OracleDbManagerBll(offLineTaskParam.dbInfo).ExecuteSqlSimple(sql);

                if (dataTable.Rows.Count <= 0) return 0;//查不到数据就结束

                dataTable.TableName = offLineTaskParam.TableName;

                new OffLineDataBll().InsertData(new OffLineData()
                {
                    SortNo = index,
                    OffLineId = offLineId,
                    DataTable = dataTable,
                    CreateTime = DateTime.Now,
                    CompressType = offLineTaskParam.DataCompressType
                });

                return dataTable.Rows.Count;
            }
            catch (Exception ex)
            {
                if (offLineTaskParam.ExceptionTryNum > 0)
                {
                    if (offLineTaskParam.ExceptionTryNum == offLineTaskParam.NowExceptionTryNum)
                    {
                        offLineTaskParam.Status = 5;
                        // throw ex;
                    }
                    else
                    {
                        offLineTaskParam.NowExceptionTryNum++;//记录异常次数
                        offLineTaskParam.Status = 6;
                        Thread.Sleep(offLineTaskParam.ExceptionWaitTime * 1000);//还未达到异常尝试次数，则再来一次
                        GetDataAndSave(sql, index, offLineId, offLineTaskParam);
                    }
                }
                throw ex;
            }
        }


        /// <summary>
        /// 检查任务状态，暂停、取消、正常
        /// </summary>
        /// <param name="offLineId"></param>
        /// <returns></returns>
        private bool CheckTaskStatus(OffLineTaskParam offLineTaskParam)
        {
            if (offLineTaskParam.Status == 3 || offLineTaskParam.Status == 6)//暂停
            {
                while (true)//挂起进程
                {
                    if (offLineTaskParam.Status == 1) break;//继续运行
                    if (offLineTaskParam.Status == 4) return false;
                }
                return true;
            }
            else if (offLineTaskParam.Status == 0 || offLineTaskParam.Status == 4)//取消
            {
                return false;
            }
            else
            {
                return true;
            }
        }



        /// <summary>
        /// 创建离线数据项
        /// </summary>
        /// <param name="tableName"></param>
        /// <returns></returns>
        private int CreateOffLine(OffLineTaskParam offLineTaskParam)
        {
            string tableName = "";//表名
            int rowNum = 0;//数据总行数
            string comments = "";//表注释
            string owner = offLineTaskParam.DataBaseName;//实例名

            var tableList = new OracleDbManagerBll(offLineTaskParam.dbInfo).GetTables(offLineTaskParam.DataBaseName, offLineTaskParam.TableName);
            if (tableList != null && tableList.Rows.Count > 0)
            {
                var rows = tableList.Select("NUM_ROWS>0", "NUM_ROWS DESC");
                if (rows.Length > 0)
                {
                    tableName = rows[0]["TABLE_NAME"] is DBNull ? "" : rows[0]["TABLE_NAME"].ToString();
                    rowNum = rows[0]["NUM_ROWS"] is DBNull ? 0 : Convert.ToInt32(rows[0]["NUM_ROWS"]);
                    comments = rows[0]["COMMENTS"] is DBNull ? "" : rows[0]["COMMENTS"].ToString();
                    owner = rows[0]["OWNER"] is DBNull ? "" : rows[0]["OWNER"].ToString();
                }
            }

            DevelopTool.Model.OffLineModels.OffLine offLine = new DevelopTool.Model.OffLineModels.OffLine()
            {
                NodeName = tableName,
                DbInfoId = offLineTaskParam.dbInfo.Id,
                DbInfoName = offLineTaskParam.dbInfo.NodeName,
                DataBaseName = owner,
                FunctionName = "批量离线",
                TableName = tableName,
                Remark = comments,
                CreateTime = offLineTaskParam.StartTime,
                NumRows = rowNum
            };
            int offLineId = new OffLineBll().Insert(offLine);//保存离线项，并返回id

            //设置离线任务全局参数
            //拼接任务名
            if (!string.IsNullOrWhiteSpace(offLine.Remark))
            {
                offLineTaskParam.TaskName = offLine.NodeName + "【" + offLine.Remark + "】";
            }
            else
            {
                offLineTaskParam.TaskName = offLine.NodeName;
            }
            offLineTaskParam.TotalRowCount = rowNum;

            return offLineId;
        }


        /// <summary>
        /// 记录日志
        /// </summary>
        /// <param name="error"></param>
        private void Log(Exception error)
        {
            string strDateInfo = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss") + "\r\n" + "出现应用程序未处理的异常：\r\n";
            string str = string.Format(strDateInfo + "异常类型：{0}\r\n异常消息：{1}\r\n异常信息：{2}\r\n",
                 error.GetType().Name, error.Message, error.StackTrace);
            LogHelper.WriteErrLog(str); //写日志
        }


    }


}
